“别把我跟你混为一谈,我努力过,所以我不后悔。” 
 
写在前面 
分享一些Ansible中Playbook执行顺序控制的手段以及运行选定的任务的笔记 
不知道小伙伴们有么有遇到这样的情况 
一些运维场景,Github中找了很棒的剧本或者角色,但是只需要其中的一部分 
一般情况下只能重新编辑(注释或者删掉)剧本处理,往往需要多次调整剧本,很麻烦 
亦或是一个有角色的剧本,你希望先执行任务,在执行角色(默认角色总是先执行) 
亦或是某些剧本你希望脱离编写顺序执行,自定义执行顺序 
亦或是你希望同时通知多个handler,处理程序被通知后立即执行,而不是等role、tasks执行完统一执行等等 
上面的问题都有解决办法,但是Ansible本身提供了很多更优的解决方法,通过博文内容一起来学习下,涉及内容:
通过标记tags仅运行标有特定标签的任务,或者从特定的任务开始执行Playbook 
通过include_role && import_role 作为任,控制角色执行顺序 
通过pre_task || post_task控制任务执行前后的回调处理 
通过listen来监听多个handlers 
通过meta: flush_handlers立即运行通知的handlers 
 
 
食用方式
了解Ansible基础知识 
可以编写 Ansible Playbook、role 
了解role构成,剧本常见指令(语法) 
 
 
理解不足小伙伴帮忙指正 
 
博文使用的ansibler版本
1 2 3 4 5 6 7 8 $ansible  --versionansible 2.8.0rc1   config file = /etc/ansible/ansible.cfg   configured module search path = ['/home/student/.ansible/plugins/modules' , '/usr/share/ansible/plugins/modules' ]   ansible python module location = /usr/lib/python3.6/site-packages/ansible   executable location = /usr/bin/ansible   python version = 3.6.8 (default, Apr  3 2019, 17:26:03) [GCC 8.2.1 20180905 (Red Hat 8.2.1-3)] $ 
 
 如果我会发光,就不必害怕黑暗。——王小波 
 
对 Ansible 剧本资源打标签 在处理大型或复杂的剧本时,如果只希望运行部分剧本或部分任务。可以将标签应用于可能要跳过或运行的特定资源。
通过标签来标记资源,在资源上使用tags关键字,然后是要应用的标记列表。在Ansible中tags标记可用于下列资源:
每个任务,这是使用标签的最常见方式之一。 
整个剧本,在剧本级别使用标签指令。 
标记include_tasks任务。include_tasks加载的所有任务都与此标签关联。 
角色,角色中的所有任务都与此标签关联。 
任务块,块中的所有任务都与此标签关联。 
 
看一个Demo,上面的标记依次来看体验下。在这之前,先准备一个角色,角色做一个echo的动作,
1 2 3 4 5 6 7 8 9 $ansible -galaxy  init tag_role --init-path=roles- tag_role was created successfully $ansible -galaxy  list | grep tag- tag_role, (unknown version) $cat  roles/tag_role/tasks/main.yml--- - name: tags roles   shell: echo  'tasks  for tag_role'  
 
编写一个剧本,在不同剧本资源执行块打上标签
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 --- -  name:  tags  Demo  1   hosts:  servera       tags:      -  play-tag-1    roles:      -  role:  tag_role               tags:          -  role-tags    tasks:      -  name:  task  1  tag        shell:  echo  'tags to task 1'               tags:          -  task-tags-1      -  name:  include  or  import  a   tasks  file        include_tasks:          file:  tasks_file               tags:          -  include-import      -  block:        -  name:  task  1  in  block          shell:  echo  'task 1 in block'        -  name:  task  2  in  block          shell:  echo  'task 2 in block'               tags:          -  block-tags  -  name:  tags  Demo  2   hosts:  servera       tags:      -  play-tag-2    tasks:      -  name:  task  2  tag        shell:  echo  'tags to task 2'        tags:          -  task-tag-2  
 
执行上面编写剧本,默认情况下打了标签,如果没有显示的指令或者设置特殊的标签,剧本默认依旧按照没打标签的顺序执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 $ansible -playbook tags.yamlPLAY [tags Demo 1] ************************************************************************************ TASK [Gathering Facts] ******************************************************************************** ok: [servera] TASK [tag_role : tags roles] ************************************************************************** changed: [servera] TASK [task 1 tag] ************************************************************************************* changed: [servera] TASK [include or import a  tasks file] **************************************************************** included: /home/student/DO447/labs/task-execution/tasks_file for  servera TASK [task 1] ***************************************************************************************** changed: [servera] TASK [task 2] ***************************************************************************************** changed: [servera] TASK [task 1 in  block] ******************************************************************************** changed: [servera] TASK [task 2 in  block] ******************************************************************************** changed: [servera] PLAY [tags Demo 2] ************************************************************************************ TASK [Gathering Facts] ******************************************************************************** ok: [servera] TASK [task 2 tag] ************************************************************************************* changed: [servera] PLAY RECAP ******************************************************************************************** servera                    : ok=10   changed=7    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 $ 
 
下面来看下如何管理标记资源,选择执行剧本资源
管理标记的资源 要列出 Playbook 中的所有标记,使用--list-tags选项
1 2 3 4 5 6 7 8 9 10 $ansible -playbook tags.yaml  --list-tagsplaybook: tags.yaml   play        TASK TAGS: [block-tags, include-import, play-tag-1, role-tags, task-tags-1]   play        TASK TAGS: [play-tag-2, task-tag-2] $ 
 
可以看到,上面的剧本的标签构成:
剧本tags Demo 1 包含标签TASK TAGS: [block-tags, include-import, play-tag-1, role-tags, task-tags-1] 
剧本tags Demo 2 包含标签play-tag-2, task-tag-2 
 
当希望运行特定的剧本资源时,给对应的资源标记打标签,然后使用ansible-playbook运行playbook时,添加--tags选项来筛选 playbook 仅运行带有特定标签的play 或任务。
1 2 3 4 5 6 7 8 9 $ansible -playbook tags.yaml  --tags=play-tag-2PLAY [tags Demo 1] ************************************************************************************************* PLAY [tags Demo 2] ************************************************************************************************* TASK [Gathering Facts] ********************************************************************************************* ok: [servera] TASK [task 2 tag] ************************************************************************************************** changed: [servera] PLAY RECAP ********************************************************************************************************* servera                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   
 
运行指令里添加了 --tags=play-tag-2,即只运行剧本tags Demo 2,当需要运行多个标签时,之间逗号隔开
1 2 3 4 5 6 7 8 9 10 11 12 $ansible -playbook tags.yaml  --tags=block-tags,role-tagsPLAY [tags Demo 1] ************************************************************************************************* TASK [tag_role : tags roles] *************************************************************************************** changed: [servera] TASK [task 1 in  block] ********************************************************************************************* changed: [servera] TASK [task 2 in  block] ********************************************************************************************* changed: [servera] PLAY [tags Demo 2] ************************************************************************************************* PLAY RECAP ********************************************************************************************************* servera                    : ok=3    changed=3    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 $ 
 
当希望运行大多数剧本资源,个别剧本资源不运行,可以在运行ansible-playbook命令时,使用--skip-tags选项跳过带有特定标签的任务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $ansible -playbook tags.yaml  --list-tagsplaybook: tags.yaml   play        TASK TAGS: [block-tags, include-import, play-tag-1, role-tags, task-tags-1]   play        TASK TAGS: [play-tag-2, task-tag-2]       $ansible -playbook tags.yaml  --skip-tags play-tag-1PLAY [tags Demo 1] ************************************************************************************************* PLAY [tags Demo 2] ************************************************************************************************* TASK [Gathering Facts] ********************************************************************************************* ok: [servera] TASK [task 2 tag] ************************************************************************************************** changed: [servera] PLAY RECAP ********************************************************************************************************* servera                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0   $ 
 
特殊的标签 如果有些剧本资源,你希望它始终运行,或是希望它始终不运行,即使在你使用tags、skip-tags指定标签的情况下,Ansible 这两种场景中提供了特殊标记:
always:带有 always 标记的资源始终都会运行,除非明确指定--skip-tags always选项。 
never:带有 never 特殊标记的资源不会运行,除非明确指定--tags never选项。 
 
看一个Demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 $cat  tags-all.yaml --- -  name:  tags  Demo  1   hosts:  servera    tags:      -  play-tag-1      -  never    roles:      -  role:  tag_role        tags:          -  role-tags    tasks:      -  name:  task  1  tag        shell:  echo  'tags to task 1'        tags:          -  task-tags-1      -  name:  include  or  import  a   tasks  file        include_tasks:          file:  tasks_file        tags:          -  include-import      -  block:        -  name:  task  1  in  block          shell:  echo  'task 1 in block'        -  name:  task  2  in  block          shell:  echo  'task 2 in block'        tags:          -  block-tags  -  name:  tags  Demo  2   hosts:  servera    tags:      -  always    tasks:      -  name:  task  2  tag        shell:  echo  'tags to task 2'  
 
可以看到剧本1设置never标签,所以默认总不会执行,剧本2设置always,所以默认总会执行
1 2 3 4 5 6 7 8 9 10 $ansible -playbook  tags-all.yamlPLAY [tags Demo 1] ************************************************************************************************* PLAY [tags Demo 2] ************************************************************************************************* TASK [Gathering Facts] ********************************************************************************************* ok: [servera] TASK [task 2 tag] ************************************************************************************************** changed: [servera] PLAY RECAP ********************************************************************************************************* servera                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 $ 
 
执行输出可以看到,剧本1没有执行,剧本2默认执行,这里,可能有小伙伴会说,如果我希望打标签的资源全部执行或者全部不执行,但是我的标签太多了,都写上很麻烦,况且我还有一些没有打标签的任务,我应该如何处理,Ansible在这些场景中提供了一些指令参数。
命令行指定标签时的特定参数:
tagged 标记将运行任何带有显式标记的资源 
untagged 标记将运行不带有显式标记的资源 
all 参数将包括 Play 中的所有任务,无论是否带有标记,这是默认行为。 
 
在来看看 Demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 $cat   tags-all.yaml --- -  name:  tags  Demo  1   hosts:  servera    tags:      -  play-tag-1    roles:      -  role:  tag_role        tags:          -  role-tags    tasks:      -  name:  task  1  tag        shell:  echo  'tags to task 1'        tags:          -  task-tags-1      -  name:  include  or  import  a   tasks  file        include_tasks:          file:  tasks_file        tags:          -  include-import      -  block:        -  name:  task  1  in  block          shell:  echo  'task 1 in block'        -  name:  task  2  in  block          shell:  echo  'task 2 in block'        tags:          -  block-tags  -  name:  tags  Demo  2   hosts:  servera    tasks:      -  name:  task  2  tag        shell:  echo  'tags to task 2'  
 
tagged 标记将运行任何带有显式标记的资源,会发现,剧本 tags Demo 2的task 2 tag任务没有标签,所以没有执行 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 $ansible -playbook  tags-all.yaml  --tags=taggedPLAY [tags Demo 1] ************************************************************************************************* TASK [Gathering Facts] ********************************************************************************************* ok: [servera] TASK [tag_role : tags roles] *************************************************************************************** changed: [servera] TASK [task 1 tag] ************************************************************************************************** changed: [servera] TASK [include or import a  tasks file] ***************************************************************************** included: /home/student/DO447/labs/task-execution/tasks_file for  servera TASK [task 1] ****************************************************************************************************** changed: [servera] TASK [task 2] ****************************************************************************************************** changed: [servera] TASK [task 1 in  block] ********************************************************************************************* changed: [servera] TASK [task 2 in  block] ********************************************************************************************* changed: [servera] PLAY [tags Demo 2] ************************************************************************************************* TASK [Gathering Facts] ********************************************************************************************* ok: [servera] PLAY RECAP ********************************************************************************************************* servera                    : ok=9    changed=6    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 
 
untagged 标记将运行不带有显式标记的资源,会发现只有剧本 tags Demo 2的task 2 tag任务执行了,因为他没有标签
1 2 3 4 5 6 7 8 9 10 $ansible -playbook  tags-all.yaml  --tags=untaggedPLAY [tags Demo 1] ************************************************************************************************* PLAY [tags Demo 2] ************************************************************************************************* TASK [Gathering Facts] ********************************************************************************************* ok: [servera] TASK [task 2 tag] ************************************************************************************************** changed: [servera] PLAY RECAP ********************************************************************************************************* servera                    : ok=2    changed=1    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 $ 
 
控制任务执行 角色最先执行 在Playbook中,Ansible 始终先执行角色中的任务,然后执行在tasks部分下定义的任务,来看一个Demo
1 2 3 4 5 6 7 8 9 10 11 12 13 14 $cat  deploy_apache_demo.yml --- -  name:  Ensure  Apache  is  deployed   hosts:  all    gather_facts:  no    tasks:      -  name:  Open  the  firewall        firewalld:          service:  http          permanent:  yes          state:  enabled    roles:      -  role:  apache  
 
上面的执行,可以看到执行数据顺序为,先执行firewall角色,然后执行apache角色,最后执行的是 Open the firewall,
1 2 3 4 5 6 7 8 9 10 11 12 13 PLAY [Ensure Apache is deployed] ********************************************************************* ..... TASK [firewall : Ensure Firewall Sources Configuration] ********************************************** ..... TASK [apache : Ensure httpd packages are installed] ************************************************** ..... TASK [apache : Ensure SELinux allows httpd connections to a remote database] ************************* ..... TASK [apache : Ensure httpd service is started and enabled] ****************************************** ..... TASK [Open the firewall] ***************************************************************************** ..... PLAY RECAP ******************************************************************************************* 
 
剧本里没有定义firewall,为什么会执行,是因为apache角色依赖了他,可以在apache角色的meta目录的maia.ymal 文件下面看到,它依赖了firewall角色
1 2 3 4 5 6 $cat  roles/apache/meta/main.yml  | grep -C 2 firewalldependencies:   - name: firewall       $ 
 
所以不管剧本编写顺序如何,同一剧本中执行顺序为,依赖角色要在当前角色之前执行,当前角色role要在调用剧本任务task之前执行。
为了剧本的可读性,一般情况下,剧本任务是写在角色后面的,整个书写顺序也就是执行顺序。
那么,如果希望在角色执行前执行任务,应该如何处理,有两种方法
其一是使用task钩子,类似生命周期中的回调函数一样, 
另一钟方法,即下面提到的,使用import或者include,关于这两个动作,小伙伴们一定不陌生,前端常见的模板引擎一般都会涉及到。不同的动作,实相同的功能,但是原理是不同的 
 
import 或 include 作为任务的角色 Ansible的最新版本允许将角色作为任务来包含或导入,而不是使用play中的角色部分。通过这样的方式,可以使剧本按照编写的顺序执行,而不是先执行角色的方式。
优点是可以按照编写顺序运行一组任务、导入或包含一个角色,然后运行更多的任务。 
缺点是,在没有仔细检查的情况下,可能不太清楚您的剧本使用的是哪些角色,因为角色切入了任务内部 
 
import和include 有些许区别
使用include_role模块可以动态包含角色, 
使用import_role模块则可静态导入角色。 
 
创建一个角色,执行的任务为打印当前的主机名
1 2 3 $ansible -galaxy init role_tasks_demo$echo  "- shell: hostname"  > roles/role_tasks_demo/tasks/main.yml 
 
编写剧本,两种不同的方式引入角色
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 --- -  name:  Executing  a  role  as  a  task   hosts:  a_web_servers    tasks:       -  name:  A  normal  task         debug:           msg:  'first task'       -  name:  A  task  to  import_role  role_tasks_demo    here         import_role:           name:  role_tasks_demo       -  name:  A  task  to  include  role_tasks_demo    here         include_role:           name:  role_tasks_demo       -  name:  Another  normal  task         debug:           msg:  'second task'  
 
执行剧本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 $ansible -playbook  role_tasks.yamlPLAY [Executing a role as a task] ******************************************************************** TASK [Gathering Facts] ******************************************************************************* ok: [serverb.lab.example.com] TASK [A normal task] ********************************************************************************* ok: [serverb.lab.example.com] => {     "msg" : "first task"  } TASK [role_tasks_demo : shell] *********************************************************************** changed: [serverb.lab.example.com] TASK [A task to include role_tasks_demo   here] ****************************************************** TASK [role_tasks_demo : shell] *********************************************************************** changed: [serverb.lab.example.com] TASK [Another normal task] *************************************************************************** ok: [serverb.lab.example.com] => {     "msg" : "second task"  } PLAY RECAP ******************************************************************************************* serverb.lab.example.com    : ok=5    changed=2    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 
 
可以看到,按照任务编写的顺序执行,角色执行了两次,这里需要注意一点,通过 import_role 方式导入的角色并会作为当前剧本的一部分,而通过 include 的方式会作为一个单独的任务模块来执行,我么通过执行的输出也可以看到,具体的原因:
使用import_role时,ansible-playbook 命令首先解析角色并插入到play中,然后开始执行。Ansible 会立即检测和报告语法错误,不会开始执行playbook。
 
使用 include_role 时,Ansible 会在 play 执行期间到达 include_role 任务时解析角色并插⼊到 play 中。如果Ansible 检测到角色中存在语法错误,则中止执行 playbook 。
 
 
对于 when 指令的行为有所不同。使用include_role任务时,如果when指令中的条件为 false,则 Ansible不解析角色。看一个Demno
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 $cat  role_tasks.yaml --- -  name:  Executing  a  role  as  a  task   hosts:  servera    tasks:       -  name:  A  normal  task         debug:           msg:  'first task'       -  name:  import_role  role         import_role:           name:  role_tasks_demo         when:  false       -  name:  include_role  role         include_role:           name:  role_tasks_demo       -  name:  Another  normal  task         debug:           msg:  'second task'  
 
执行我么可以看到。import_role被直接跳过了,因为when的原因,并没有执行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $ansible -playbook  role_tasks.yamlPLAY [Executing a role as a task] ********************************************************************* TASK [Gathering Facts] ******************************************************************************** ok: [servera] TASK [A normal task] ********************************************************************************** ok: [servera] => {     "msg" : "first task"  } TASK [role_tasks_demo : shell] ************************************************************************ skipping: [servera] TASK [include_role role] ****************************************************************************** TASK [role_tasks_demo : shell] ************************************************************************ changed: [servera] TASK [Another normal task] **************************************************************************** ok: [servera] => {     "msg" : "second task"  } PLAY RECAP ******************************************************************************************** servera                    : ok=4    changed=1    unreachable=0    failed=0    skipped=1    rescued=0    ignored=0 
 
角色执行前&&任务执行后的钩子 有时候希望⼀个剧本 在角色之前运行某些任务,以及它们所通知的处理程序。也可能希望在普通任务tasks和处理程序handler运行后运行 play 中的任务。
可以使用两个指令(而非 tasks)来实现这一目标:
pre_tasks 是在 roles 部分前运行的 tasks 部分。 
post_tasks 是在 tasks 部分以及 tasks 所通知的任何处理程序后运行的 tasks 部分。 
 
通过Demo来看下,创建一个测试角色,同样打印主机名
1 2 $ansible -galaxy  init tasks_hook_demo$echo  "- shell: hostname"  > roles/tasks_hook_demo/tasks/main.yml
 
编写剧本,使用 pro_taks和post_task来执行剧本之前前后的钩子任务
1 2 3 4 5 6 7 8 9 10 11 12 13 14 --- -  name:  task  task  exec  order   hosts:  a_web_servers    pre_tasks:       -  name:  pre_tasks  in  task         shell:  echo  'pre_tasks'    post_tasks:       -  name:  post_tasks  in  taks         shell:  echo  'post_tasks'    tasks:       -  name:  task         shell:  echo  'tasks'    roles:      -  tasks_hook_demo  
 
可以看到编写顺序并不会影响到执行顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 $ansible -playbook  tasks_hook.yamlPLAY [task task exec  order] ************************************************************************** TASK [Gathering Facts] ******************************************************************************* ok: [serverb.lab.example.com] TASK [pre_tasks in  task] ***************************************************************************** changed: [serverb.lab.example.com] TASK [tasks_hook_demo : shell] *********************************************************************** changed: [serverb.lab.example.com] TASK [task] ****************************************************************************************** changed: [serverb.lab.example.com] TASK [post_tasks in  taks] **************************************************************************** changed: [serverb.lab.example.com] PLAY RECAP ******************************************************************************************* serverb.lab.example.com    : ok=5    changed=4    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 
 
上面的执行顺序为 pre_tasks > roles >  tasks >  post_tasks,不管语句顺序如何变化,都不会改变执行顺序
这里,有小伙伴会想到,如果任务中有handlers应该如何处理? 
有handlers的执行顺序 Ansible 按照以下顺序运行 Play 的不同部分:
pre_tasks 
pre_tasks 部分中通知的处理程序 handlers 
roles 
tasks 
roles 和 tasks 部分中通知的处理程序 handlers 
post_tasks 
post_tasks 部分中通知的处理程序 handlers 
 
这些部分在 Play 中的编写顺序不会修改以上列出的执行顺序。来看一个demo
这里先创建一个角色,使用shell 模块打印一句话 
1 2 3 4 5 6 7 8 9 10 11 12 $ansible -galaxy  init task_liruilong_exec_order --init-path=roles- task_liruilong_exec_order was created successfully $ansible -galaxy  list | grep liruilo- task_liruilong_exec_order, (unknown version) $tee   roles/task_liruilong_exec_order/tasks/main.yml <<- EOF > --- > - name: role: task_liruilong_exec_order >   shell: echo "roles  liruilong" > EOF --- - name: role task_liruilong_exec_order   shell: echo  "roles  liruilong"  
 
然后我在角色中添加一个通知,在角色的handlers文件夹中定义执行的事件,在taks发生chagent的时候会触发通知,执行通知的handler
1 2 3 4 5 6 7 8 9 10 11 12 13 14 $vim  roles/task_liruilong_exec_order/tasks/main.yml$cat  roles/task_liruilong_exec_order/tasks/main.yml--- - name: role  task_liruilong_exec_order   shell: echo  "roles  liruilong"    notify:     - role_handler $vim  roles/task_liruilong_exec_order/handlers/main.yml$cat  roles/task_liruilong_exec_order/handlers/main.yml--- - name: role_handler   shell: echo  'role in role_handler'  $vim  task_order.yaml
 
来看下的剧本,在pre_tasks和post_tasks,tasks中,都有剧本中handler的notify。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 $cat  task_order.yaml --- -  name:  tesk  task  exec  order   hosts:  servera    pre_tasks:       -  name:  task  in  pre_taks         shell:  echo  'task in pre_tasks'         notify:           -  ploybook_in_handler    roles:       -  role:  task_liruilong_exec_order    tasks:       -  name:  task  in  tasks         shell:  echo  'task in tasks'         notify:           -  ploybook_in_handler    post_tasks:       -  name:  taks  in  post_tasks         shell:  echo  'task in post_tasks'         notify:           -  ploybook_in_handler    handlers:       -  name:  ploybook_in_handler         shell:  echo  ' ploybook in handle'  
 
执行剧本,可以看到剧本你的执行顺序为:pre_taks > pre_taks  handler > role > tasks > role handler > tasks handler > post_tasks > post_tasks handler
1 2 3 4 5 6 7 8 9 10 11 12 $ansible -playbook  task_order.yamlPLAY [tesk task exec  order] ************************************************************************** TASK [Gathering Facts] *******************************************************************************ok: [servera] TASK [task in  pre_taks] ******************************************************************************changed: [servera] RUNNING HANDLER [ploybook_in_handler] ****************************************************************changed: [servera] TASK [task_liruilong_exec_order : role  task_liruilong_exec_order] ***********************************changed: [servera] TASK [task in  tasks] *********************************************************************************changed: [servera] RUNNING HANDLER [task_liruilong_exec_order : role_handler] *******************************************changed: [servera] RUNNING HANDLER [ploybook_in_handler] ****************************************************************changed: [servera] TASK [taks in  post_tasks] ****************************************************************************changed: [servera] RUNNING HANDLER [ploybook_in_handler] ****************************************************************changed: [servera] PLAY RECAP *******************************************************************************************servera                    : ok=9    changed=8    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 
 
这里要说明的是剧本中这些块的顺序不会改变执行顺序。一个剧本包含pre_tasks,roles,tasks,post tasks和handlers部分是不寻常的。如果在多个部分中得到通知,则处理程序handler可以在剧本执行期间的不同时间多次运行。但是不会存在 一个处理的程序连续执行多次,即同一时间段,多此次通知只执行一次。
那么这里的话,如果希望某一任务在发生change后,同时通知多个处理程序handler,应该如何处理 
任务一次性通知多个handlers任务 
按名称通知handlers列表。 
通知配置了listen来监听多个handlers 
 
下面的剧本中,notify部分并没有发生改变,但是handlers部分添加一个listen属性,即由原来的通知变成了监听,这里其实有点类似观察者设计模式的两种实现方式,当观察者的数据较少的时候,采用的是推的一种方式,较多时,通过拉的方式实现。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 --- -  name:  tesk  task  exec  order   hosts:  servera    pre_tasks:       -  name:  task  in  pre_taks         shell:  echo  'task in pre_tasks'         notify:           -  ploybook_in_handler    roles:       -  role:  task_liruilong_exec_order    tasks:       -  name:  task  in  tasks         shell:  echo  'task in tasks'         notify:           -  ploybook_in_handler    post_tasks:       -  name:  taks  in  post_tasks         shell:  echo  'task in post_tasks'         notify:           -  ploybook_in_handler    handlers:       -  name:  ploybook_in_handler  1         shell:  echo  ' ploybook in handle 1'         listen:  ploybook_in_handler       -  name:  ploybook_in_handler  2         shell:  echo  ' ploybook in handle 2'         listen:  ploybook_in_handler  
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 $ansible -playbook  task_order.yamlPLAY [tesk task exec  order] *************************************************************************** TASK [Gathering Facts] ******************************************************************************** ok: [servera] TASK [task in  pre_taks] ******************************************************************************* changed: [servera] RUNNING HANDLER [ploybook_in_handler 1] *************************************************************** changed: [servera] RUNNING HANDLER [ploybook_in_handler 2] *************************************************************** changed: [servera] TASK [task_liruilong_exec_order : role  task_liruilong_exec_order] ************************************ changed: [servera] TASK [task in  tasks] ********************************************************************************** changed: [servera] RUNNING HANDLER [task_liruilong_exec_order : role_handler] ******************************************** changed: [servera] RUNNING HANDLER [ploybook_in_handler 1] *************************************************************** changed: [servera] RUNNING HANDLER [ploybook_in_handler 2] *************************************************************** changed: [servera] TASK [taks in  post_tasks] ***************************************************************************** changed: [servera] RUNNING HANDLER [ploybook_in_handler 1] *************************************************************** changed: [servera] RUNNING HANDLER [ploybook_in_handler 2] *************************************************************** changed: [servera] PLAY RECAP ******************************************************************************************** servera                    : ok=12   changed=11   unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 
 
当然,有小伙伴说,我不嫌麻烦,我就想每次推,那么应该如何实现,可以在notafy写成一个list
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $cat   roles/task_liruilong_exec_order/tasks/main.yml--- - name: role  task_liruilong_exec_order   shell: echo  "roles  liruilong"    notify:     - role_handler1     - role_handler2 $cat  roles/task_liruilong_exec_order/handlers/main.yml--- - name: role_handler1   shell: echo  'role in role_handler 1'  - name: role_handler2   shell: echo  'role in role_handler 2'  $ 
 
这样也是可以的
1 2 3 4 5 6 7 8 9 10 11 12 13 14 $ansible -playbook task_order.yamlPLAY [tesk task exec  order] *************************************************************************** TASK [Gathering Facts] ******************************************************************************** ok: [servera] ......... TASK [task_liruilong_exec_order : role  task_liruilong_exec_order] ************************************ changed: [servera] TASK [task in  tasks] ********************************************************************************** changed: [servera] RUNNING HANDLER [task_liruilong_exec_order : role_handler1] ******************************************* changed: [servera] RUNNING HANDLER [task_liruilong_exec_order : role_handler2] ******************************************* changed: [servera] ..... 
 
在上面的Demo中,可以看到,任务处理程序 handler 总是在一个任务块处理完之后才会执行,那如果我希望在任务执行到一半,执行前面的任务处理程序,应该这么处理?
立即运行剧本中特定任务通知的任何处理程序 若要立即运行由 Play 中特定任务通知的任何处理程序,可以添加一个使用 meta 模块及 flush_handlers 参数任务:
meta: flush_handlers
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 --- -  name:  tesk  task  exec  order   hosts:  servera    pre_tasks:       -  name:  task  in  pre_taks         shell:  echo  'task in pre_tasks'         notify:           -  ploybook_in_handler    roles:       -  role:  task_liruilong_exec_order    tasks:       -  name:  task  in  tasks  1         shell:  echo  'task in tasks 1'         notify:           -  ploybook_in_handler       -  meta:  flush_handlers       -  name:  task  in  tasks  2         shell:  echo  'task in tasks 2'         notify:           -  ploybook_in_handler    post_tasks:       -  name:  taks  in  post_tasks         shell:  echo  'task in post_tasks'         notify:           -  ploybook_in_handler    handlers:       -  name:  ploybook_in_handler  1         shell:  echo  ' ploybook in handle 1'         listen:  ploybook_in_handler       -  name:  ploybook_in_handler  2         shell:  echo  ' ploybook in handle 2'         listen:  ploybook_in_handler  
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 $ ansible-playbook  task_order.yaml PLAY [tesk task exec  order] ************************************************************************** TASK [Gathering Facts] ******************************************************************************* ok: [servera] TASK [task in  pre_taks] ****************************************************************************** changed: [servera] RUNNING HANDLER [ploybook_in_handler 1] ************************************************************** changed: [servera] RUNNING HANDLER [ploybook_in_handler 2] ************************************************************** changed: [servera] TASK [task_liruilong_exec_order : role  task_liruilong_exec_order] *********************************** changed: [servera] TASK [task in  tasks 1] ******************************************************************************* changed: [servera] RUNNING HANDLER [task_liruilong_exec_order : role_handler1] ****************************************** changed: [servera] RUNNING HANDLER [task_liruilong_exec_order : role_handler2] ****************************************** changed: [servera] RUNNING HANDLER [ploybook_in_handler 1] ************************************************************** changed: [servera] RUNNING HANDLER [ploybook_in_handler 2] ************************************************************** changed: [servera] TASK [task in  tasks 2] ******************************************************************************* changed: [servera] RUNNING HANDLER [ploybook_in_handler 1] ************************************************************** changed: [servera] RUNNING HANDLER [ploybook_in_handler 2] ************************************************************** changed: [servera] TASK [taks in  post_tasks] **************************************************************************** changed: [servera] RUNNING HANDLER [ploybook_in_handler 1] ************************************************************** changed: [servera] RUNNING HANDLER [ploybook_in_handler 2] ************************************************************** changed: [servera] PLAY RECAP ******************************************************************************************* servera                    : ok=16   changed=15   unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 $ 
 
控制主机执行顺序 Ansible根据剧本hosts指令确定要管理的主机。默认情况下,Ansible2.4和更高版本根据清单中主机列出的顺序运行剧本。您可以使用order指令更改该顺序。
order指令接受以下值:
inventory 清单顺序。这是默认值。 
reverse_inventory 清单相反顺序。 
sorted 主机按字母顺序排列。数字在字母前排序。 
reverse_sorted 主机以相反的字母顺序排列。 
shuffle 每次您执行剧本时,随机排序。 
 
由于Ansible通常在多个主机上并行运行每个任务,因此 ansible-playbook 命令的输出可能无法反映预期的顺序:输出显示的是任务完成顺序,而不是执行顺序。
来看一个Demo,下面的配置中,定义了order: sorted的顺序执行主机
1 2 3 4 5 6 7 8 9 $ cat  host-all.yaml --- - name: Executing a role as a task   hosts: all   order: sorted   tasks:      - name: A normal task        debug:          msg: 'first task'  
 
可以看到执行顺序即为字母顺序
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 $ ansible-playbook  host-all.yaml -f 1 PLAY [Executing a role as a task] ********************************************************************* TASK [Gathering Facts] ******************************************************************************** ok: [servera] ok: [serverb] ok: [serverc] ok: [serverd] ok: [servere] ok: [serverf] TASK [A normal task] ********************************************************************************** ok: [servera] => {     "msg" : "first task"  } ok: [serverb] => {     "msg" : "first task"  } ok: [serverc] => {     "msg" : "first task"  } ok: [serverd] => {     "msg" : "first task"  } ok: [servere] => {     "msg" : "first task"  } ok: [serverf] => {     "msg" : "first task"  } PLAY RECAP ******************************************************************************************** servera                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 serverb                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 serverc                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 serverd                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 servere                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 serverf                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 
 
主机执行顺序为随机执行
1 2 3 4 5 6 7 8 9 10 $  cat  host-all.yaml --- -  name:  Executing  a  role  as  a  task   hosts:  all    order:  shuffle    tasks:       -  name:  A  normal  task         debug:           msg:  'first task'  
 
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 $ ansible-playbook  host-all.yaml -f 1 PLAY [Executing a role as a task] ********************************************************************* TASK [Gathering Facts] ******************************************************************************** ok: [serverb] ok: [serverc] ok: [servere] ok: [serverf] ok: [serverd] ok: [servera] TASK [A normal task] ********************************************************************************** ok: [serverf] => {     "msg" : "first task"  } ok: [servera] => {     "msg" : "first task"  } ok: [servere] => {     "msg" : "first task"  } ok: [serverd] => {     "msg" : "first task"  } ok: [serverb] => {     "msg" : "first task"  } ok: [serverc] => {     "msg" : "first task"  } PLAY RECAP ******************************************************************************************** servera                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 serverb                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 serverc                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 serverd                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 servere                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 serverf                    : ok=2    changed=0    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 
 
来看一个具体的实际应用。
实战 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 $  cat  deploy_haproxy.yml -  name:  Ensure  HAProxy  is  deployed   hosts:  lb_servers    force_handlers:  True    pre_tasks:      -  name:  Setting  the  maintenance  message        copy:          dest:  /etc/motd.d/maintenance          content:  "Maintenance in progress\n"    roles:                -  role:  haproxy    post_tasks:      -  name:  Removing  the  maintenance  message        file:          path:  /etc/motd.d/maintenance          state:  absent    handlers:      -  name:  Sending  an  email  to  student        mail:          subject:  "HAProxy reloaded on {{ inventory_hostname }} "          to:  student@workstation.lab.example.com        delegate_to:  localhost        become:  false        listen:  reload  haproxy      -  name:  Logging  a  message  to  syslog        syslogger:          msg:  "HAProxy reloaded on {{ inventory_hostname }} "        delegate_to:  localhost        become:  false        listen:  reload  haproxy  
 
上面的剧本在执行haproxy这个角色的时候,通过pro_taks添加登录时的欢迎信息,提示系统正在维护,维护信息写到 /etc/motd.d/maintenance,同时在角色执行完后,在post_tasks中删除欢迎信息。
同时监听角色中的haproxy services文件重新加载的事件,当服务配置文件重新load的时候,发送邮件,并且写入系统日志
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 $ cat roles/haproxy/handlers/main.yml --- - name: restart haproxy   service:     name: haproxy     state: restarted - name: reload haproxy   service:     name: haproxy     state: reloaded $ 
 
执行剧本
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 $ ansible-playbook  deploy_haproxy.yml PLAY [Ensure HAProxy is deployed] ********************************************************************* TASK [Gathering Facts] ******************************************************************************** ok: [servera.lab.example.com] TASK [Setting the maintenance message] **************************************************************** changed: [servera.lab.example.com] TASK [firewall : Ensure Firewall Sources Configuration] *********************************************** ok: [servera.lab.example.com] => (item={'port' : '80/tcp' }) TASK [haproxy : Ensure haproxy packages are present] ************************************************** changed: [servera.lab.example.com] TASK [haproxy : Ensure haproxy is started and enabled] ************************************************ changed: [servera.lab.example.com] TASK [haproxy : Ensure haproxy configuration is set ] ************************************************** changed: [servera.lab.example.com] RUNNING HANDLER [haproxy : reload haproxy] ************************************************************ changed: [servera.lab.example.com] RUNNING HANDLER [Sending an email to student] ********************************************************* ok: [servera.lab.example.com] RUNNING HANDLER [Logging a message to syslog] ********************************************************* changed: [servera.lab.example.com] TASK [Removing the maintenance message] *************************************************************** changed: [servera.lab.example.com] PLAY RECAP ******************************************************************************************** servera.lab.example.com    : ok=10   changed=7    unreachable=0    failed=0    skipped=0    rescued=0    ignored=0 You have new mail in  /var/spool/mail/student 
 
登录机器可以发现系统维护的信息,等角色执行完。维护信息消失
1 2 3 4 5 6 7 8 9 10 11 12 13 $ ssh servera Activate the web console with: systemctl enable  --now cockpit.socket Maintenance in  progress [student@servera ~]$ exit  logout Connection to servera closed. You have mail in  /var/spool/mail/student $ ssh servera Activate the web console with: systemctl enable  --now cockpit.socket Last login: Sat Aug 13 01:15:44 2022 from 172.25.250.9 [student@servera ~]$ 
 
可以看到邮件信息
1 2 3 4 5 6 $ mail Heirloom Mail version 12.5 7/5/10.  Type ? for  help . "/var/spool/mail/student" : 1 message 1 new>N  1 root@workstation.lab  Sat Aug 13 01:10  27/1102  "HAProxy reloaded on servera.lab.example.com"  & q Held 1 message in  /var/spool/mail/student 
 
博文参考 《Red Hat Ansible Engine 2.8 DO447》